<script setup lang="ts">

import { markRaw, reactive, readonly, shallowReadonly, shallowRef, toRaw, toRef, toRefs } from 'vue'

const obj = reactive({
  age: 18,
  msg: {
    code: 0
  }
})
//一、 readonly 和 shallowReadonly 都会创建只读的响应式对象，防止直接修改对象的属性

// 1.1 我们先看readonly
let obj_1 = readonly(obj) // 使用readonly 将obj改为只读的
// console.log(obj_1) // 打印出来的是Proxy的响应式对象 证明是响应式的
// 1.2 readonly 会递归地将对象所有的属性都转换为只读响应式对象，包括嵌套对象的属性也是只读的
// 1.3 修改obj_1
function changeObj_1() {
  // obj.age++ // 原生的对象可以修改值
  // console.log(obj.age) // 原生的对象可以获取到
  // 当我们使用只读后的对象看能否获取/修改呢？
  // obj_1.age++ // 当我们将对象设置为只读以后 我们已经没有办法进行修改了
  console.log(obj_1.age) // 虽然设置成看只读 但是我们依旧可以获取到 只不过 获取到永远都是一个固定的值
  // 如果我们对嵌套的属性进行修改试试呢？
  // obj_1.msg.code++
  // console.log(obj_1.msg.code)
  // 经过尝试 我们发现无法对内层嵌套的对象进行修改 说明 readonly对深层嵌套的对象也是有效的
}

// 1.4 我们再来看shallowReadonly 声明浅只读响应式数据
const obj_2 = shallowReadonly(obj) // 使用shallowReadonly 将obj对象设置为只读的
// 看看这两个函数的区别
function changeObj_2() {
  // console.log(obj) // 访问原生的对象 得到的是响应式的对象 里面有我们的age和msg属性
  // console.log(obj_2) // 访问被改为只读以后的对象 与获取原生对象没有区别
  // 现在我们对被改为只读后的对象尝试进行修改
  // obj_2.age++ // 我们发现 我们无法对被改为只读后的属性进行修改
  // 如果我们对嵌套的属性进行修改试试呢？
  // 我们发现可以对象嵌套内层的对象进行修改 这就是与readonly的区别
  // obj_2.msg.code++
  // console.log(obj_2.msg.code)
}

// 一、readonly 与 shallowReadonly总结
// 1.readonly 会递归地将对象所有的属性都转换为只读响应式对象，包括嵌套对象的属性也是只读的，
// 2.shallowReadonly 只会将根级属性设置为只读响应式对象，嵌套对象的属性仍然是可修改的。
// 3.当对只读响应式对象进行修改时，都会在控制台输出警告，但并不会改变属性的值。
// 4.使用 readonly 和 shallowReadonly 可以有效地保护响应式对象的属性不被意外修改，并根据需求选择适合的方式来创建只读对象


// 二、toRaw 与 markRaw 在 Vue 3 中，toRaw 和 markRaw 是用于处理响应式对象的两个函数，它们都与 Vue 3 的响应式系统有关
// 2.1 我们先看 toRaw函数 用于获取代理对象的原始未代理版本 也就是说我们将响应式数据 转换为不是响应式的
const obj1 = reactive({
  name: '佐助',
  age: 10,
  city: {
    one: '木叶村',
    two: '宇智波家族'
  }
})
// console.log(obj1) // 此时我们已经将obj11转换为响应式
// 我们使用toRaw函数 将obj1 转换为不是响应式的数据
const oldObj1 = toRaw(obj1)
// console.log(oldObj1, '被转为不是响应式后的') // 我们发现 当不是响应式数据的时候我们得到的是一个普通的对象

// 2.2 我们再来看看markRaw 这个函数使其永远不会被转换为响应式对象
// const oldObj1_m = markRaw({
//   name: '佐助',
//   age: 10,
//   city: {
//     one: '木叶村',
//     two: '宇智波家族'
//   }
// }) // 得到的是普通的JS对象 表示已经不在是响应式了
// const oldObj1_m = markRaw(toRaw(obj1)) // 得到的是普通的JS对象表示已经不在是响应式了
const oldObj1_m = markRaw(obj1) // 依旧是响应式的
// console.log(oldObj1_m, '使用markRaw ')

//2.3 toRaw 与 markRaw总结
// toRaw 用于获取代理对象的原始未代理版本，有助于直接访问原始值，而不是代理值
// markRaw 用于标记一个对象，使其永远不会被转换为响应式对象 我们使用的使用需要将整个对象传进去 而不是保存对象的变量
// 并且markRaw可以与toRaw函数搭配使用 也就是说 markRaw只能传入纯JS对象。


// 三、shallowReactive 与 shallowRef 都是用于创建响应式对象的函数
// 3.1 shallowReactive 用于创建一个浅响应式对象，它只会对根属性进行响应式处理，而不会递归到对象的嵌套属性
// 当对浅响应式对象进行操作时，只有根属性的变化会触发视图更新，而嵌套属性的变化不会触发

// shallowRef 用于创建一个包含基本值或对象引用的 ref 对象，它也是浅的，只会监听到根属性的变化，并将变化通知给视图
// 当使用 shallowRef 包装一个对象时，对该对象内部属性的修改不会被监听，只有重新赋值整个对象的操作才会引起响应式更新

// 使用shallowReactive 声明一个对象
// let ls = shallowReactive({
let ls = shallowRef({
  age: 18,
  sex: '男',
  msg: {
    num: 0
  }
})

// 定义一个函数改变 ls
function changeLs() {
  // ls.age++ // shallowReactive   如果根属性没有发生变化 则里面嵌套的属性也不会发生变化
  // ls.msg.num++//shallowReactive 如果根属性没有发变化 则嵌套的属性不是响应式的。
  // console.log(ls.msg.num)
  // 如果我们使用shallowRef函数 则发现 JS代码会发生改变 但是页面的数据不会更新
  ls.value.age++
  ls.value.msg.num++
  console.log(ls.value.msg.num)
  console.log(ls.value.age)

  // 所以 根据概念 只有重新赋值整个对象的操作才会引起响应式更新
  // ls.value = { age: 100, msg: { num: 200 } }
  console.log('重新赋值对象之后的')
  // 我们发现 里面的值改成了我们设置好的值 这就是shallowRef函数的特点
  console.log(ls.value.age)
  ls.value.age++
  ls.value.msg.num++
  console.log(ls.value.msg.num)
}


// 四、toRefs 与 toRef都是用于创建 ref 对象的函数 但是略有不同
// 4.1 toRefs 简单来说就把响应式对象里面的属性 也变成响应式的。
const cat = reactive({
  name: 'duo',
  data: {
    age: 1
  }
})
// 我们拿到cat对象里面的name和data属性
const { name, data } = toRefs(cat)
// 我们打印name和data得到的是一个对象
// console.log(name)
// console.log(data)
// 定义修改Cat对象的方法
function setCat() {
  name.value = '迪迦'
  data.value.age++
}

// 4.2 toRef函数 该函数接收两个参数 第一个参数是响应式对象 第二个参数是响应式对象的属性
const dog = reactive({
  nickName: '旺财',
  trait: {
    // 特点
    height: 170,
    physique: '大'
  }
})
const { nickName, trait } = dog
// 我们把dog对象的 trait属性变为响应式的
const xh = toRef(dog, 'trait')
// console.log(xh)

// 修改dog对象
function setDog() {
  // 我们尝试修改一下不是响应式数据的nickName
  console.log(dog.nickName) // 我们是可以获取到对象的属性的
  // console.log(xh.value.nickName) // 由于toRef只能将单个属性改为响应式 所以我们是获取不到的
  // 如果我们修改原始对象的属性能否成功呢？
  dog.nickName = '小黑'
  console.log(dog, 'dog') // 我们发现 JS层面修改成功了 但是页面上始终没有进行同步
  // 因为我们将trait属性改为了响应式数据 所以里面的属性也是响应式数据
  xh.value.height = 180
  xh.value.physique = '很大'
}

// 四、toRefs和toRef总结
// toRef 函数可以指定响应式对象的单独属性改为响应式 语法为:const xxx = toRef(响应式对象,'响应式对象上的属性名')
// toRef 返回一个 ref 对象，可以通过该 ref 对象访问原始响应式对象的属性，修改 ref 对象的值会影响到原始对象

// toRefs函数 可以把响应式对象的全部属性 都改为响应式的 语法为:const xxx = toRefs(响应式对象)
// toRefs 返回一个对象,该对象中的每个属性都是 ref 对象,通过该 ref 对象可以访问原始响应式对象的对应属性。
// 修改 ref 对象的值会影响到原始对象 !!!
</script>

<template>
  <h1>一、readonly 与 shallowReadonly 用于创建只读响应式对象的函数。</h1>
  <h2>readonly</h2>
  <p>obj.age:{{ obj.age }}</p>
  <p>obj.age:{{ obj_1.age }}</p>
  <p>obj.age:{{ obj_1.msg.code }}</p>
  <button @click="changeObj_1">修改obj_1</button>
  <h2>shallowReadonly</h2>
  <p>obj_2:{{ obj_2.age }}</p>
  <p>obj_2:{{ obj_2.msg.code }}</p>
  <button @click="changeObj_2">修改obj_2</button>
  <p>-----------------------</p>
  <h1>二、toRaw 与 markRaw</h1>
  <h2>toRaw</h2>
  <p>是响应式的obj1对象:{{ obj1 }}</p>
  <p>不是响应式的obj1对象:{{ oldObj1 }}</p>
  <p>我们发现 不管是不是响应式的渲染的时候都是一样的</p>
  <h2>markRaw</h2>

  <p>-----------------------</p>
  <h1>三、shallowReactive 与 shallowRef</h1>
  <h2>shallowReactive--------------</h2>
  <p>ls.age:{{ ls.age }}</p>
  <p>ls.msg.num:{{ ls.msg.num }}</p>
  <button @click="changeLs">改变ls对象</button>
  <h2>shallowRef--------------</h2>
  <p>ls.age:{{ ls.age }}</p>
  <p>ls.msg.num:{{ ls.msg.num }}</p>
  <button @click="changeLs">改变ls对象</button>
  <h1>四、toRef 与 toRefs</h1>
  <h2>toRefs</h2>
  <p>{{ name }}</p>
  <p>{{ data.age }}</p>
  <button @click="setCat">修改Cat对象</button>
  <h2>toRef</h2>
  <p>不是响应式--{{ nickName }}</p>
  <p>是响应式数据--{{ trait.height }}</p>
  <p>是响应式数据--{{ trait.physique }}</p>
  <button @click="setDog">修改dog对象</button>
</template>

<style scoped>

</style>
